package com.laopan.wxpay.request;

import com.laopan.wxpay.WxConstants;
import com.laopan.wxpay.bean.FailDetails;
import com.laopan.wxpay.bean.certificate.MerchantPrivateKey;
import com.laopan.wxpay.config.WxPayConfig;
import com.laopan.wxpay.exception.WxPayException;
import com.laopan.wxpay.exception.WxPayRequestFailException;
import com.laopan.wxpay.request.certificates.DefaultPrivateKeyManager;
import com.laopan.wxpay.request.certificates.MemoryCacheCertificatesManager;
import com.laopan.wxpay.request.certificates.PrivateKeyManager;
import com.laopan.wxpay.request.certificates.WxCertificatesManager;
import com.laopan.wxpay.utils.HttpUtils;
import com.laopan.wxpay.utils.JsonUtils;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URISyntaxException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Map;


/**
 * 公用的微信支付接口請求信息
 * @author laopan
 * @className WxPayRequest
 * @date 2021/2/23 11:39
 */
public abstract  class WxPayRequest<T,R> {
    private final static Logger log = LoggerFactory.getLogger(WxPayRequest.class);

    private final String domain;
    private final WxPayConfig wxPayConfig;
    public WxPayRequest(WxPayConfig wxPayConfig) {
        this.domain= WxConstants.PAY_HOST_URL;
        this.wxPayConfig=wxPayConfig;
    }
    private String getRequestAllUrl(String requestUrl){
        return domain+requestUrl;
    }
    /**
     * 创建请求
     * @return 请求结果
     */
    public CloseableHttpClient createRequest(){
       return createRequest(true);
    }
    public CloseableHttpClient createRequest(boolean isLoadCert){
        String mchId=wxPayConfig.getMchId();
        PrivateKeyManager privateKeyManager = new DefaultPrivateKeyManager(wxPayConfig);
        final MerchantPrivateKey merchantPrivateKey = privateKeyManager.getPrivateKey(mchId);
        PrivateKey privateKey = merchantPrivateKey.getPrivateKey();
        ArrayList<X509Certificate> listCertificates = new ArrayList<>();
        if(isLoadCert){
            WxCertificatesManager certificatesManager = new MemoryCacheCertificatesManager(merchantPrivateKey);
            X509Certificate wechatPayCertificate = certificatesManager.getValidCertificate(mchId);
            listCertificates.add(wechatPayCertificate);
        }
        return WechatPayHttpClientBuilder.create()
                .withMerchant(mchId, merchantPrivateKey.getSerialNo(), merchantPrivateKey.getPrivateKey())
                .withWechatpay(listCertificates)
                .build();
    }
    public R get(String requestUrl,T body){
        return get(requestUrl,body,null);
    }
    public R get(String requestUrl){
        return get(requestUrl,null,null);
    }
    public R get(String requestUrl,T body,RequestCallback<R> requestCallback) {
        String url =getRequestAllUrl(requestUrl);
        String result ="";
        HttpGet httpGet;
        try {
            URIBuilder uriBuilder = new URIBuilder(url);
            if(body!=null){
                Map<String,String> paramMap=JsonUtils.toMap(body);
                if(paramMap!=null&&paramMap.size()>0){
                    paramMap.keySet().forEach(key ->{
                        String value = paramMap.get(key);
                        if(value!=null&&value.trim().length()>0){
                            uriBuilder.setParameter(key, value);
                        }
                    });
                }
            }
            httpGet = new HttpGet(uriBuilder.build());
            httpGet.addHeader("Accept", "application/json");
        } catch (URISyntaxException e) {
            throw new WxPayException("url错误");
        }
        return getResult(httpGet,requestCallback);
    }

    public  R post(String requestUrl, T body){
        return post(requestUrl,body,null);
    }
    public  R post(String requestUrl, T body,RequestCallback<R> requestCallback) {
        if (body == null) {
            throw new WxPayException("请求的数据不能为空");
        }
        String bodyStr =  JsonUtils.toJson(body);
        String url =getRequestAllUrl(requestUrl);
        HttpPost httpPost = new HttpPost(url);
        log.info("url:{},请求数据：{}",url,bodyStr);
        StringEntity reqEntity = new StringEntity( bodyStr, HttpUtils.getContentType());
        httpPost.setEntity(reqEntity);
        httpPost.addHeader("Accept", WxConstants.CONTENT_TYPE_JSON);
        return getResult(httpPost,requestCallback);
    }

    private R getResult(HttpUriRequest request, RequestCallback<R> requestCallback){
        String resultStr ="";
        CloseableHttpResponse response = null;
        CloseableHttpClient httpClient=null;
        try {
            httpClient = createRequest();
            response =httpClient.execute(request);
            if(requestCallback!=null){
                return requestCallback.getResult(response);
            }
            int code=response.getStatusLine().getStatusCode();
            HttpEntity entity= response.getEntity();
            if(code == HttpStatus.SC_OK){
                resultStr= EntityUtils.toString(entity);
            }else{
                String result= EntityUtils.toString(entity);
                FailDetails failDetails = JsonUtils.fromJson(result, FailDetails.class);
                throw new WxPayRequestFailException(failDetails.getCode(),failDetails.getMessage());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(response != null){
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(httpClient!=null){
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return JsonUtils.fromJson(resultStr, getResultClass());
    }

    /**
     * 获取最终结果
     * @return 类信息
     */
    protected abstract Class<R> getResultClass();

}
